package com.joshondesign.sdljava.gfx;

import com.joshondesign.sdljava.*;
import com.joshondesign.sdljava.events.Gestures;
import com.joshondesign.sdljava.events.Keyboard;
import com.joshondesign.sdljava.events.RawEvents;

import java.util.*;

import static com.joshondesign.sdljava.demo.Demo1.p;

/**
 * Created by IntelliJ IDEA.
 * User: josh
 * Date: 9/6/11
 * Time: 10:04 AM
 * To change this template use File | Settings | File Templates.
 */
public class Scene {
    private List<Layer> layers;
    private List<Animation> anims;
    private SDL_Surface surface;
    private SColor backgroundFill = SColor.WHITE;
    private ArrayList<Callback> listeners = new ArrayList<Callback>();
    private ArrayList<Callback<MouseEvent>> mouseDragListeners = new ArrayList<Callback<MouseEvent>>();
    private boolean mouseDown;
    private List<Trigger.TriggerRule> triggers = new ArrayList<Trigger.TriggerRule>();
    private boolean keydown;
    private int targetFPS = 30;
    private Map<Integer,Keyboard.Keycode> keymap;
    private boolean shouldStop;

    public Scene(SDL_Surface surface) {
        this.surface = surface;
        this.layers = new ArrayList<Layer>();
        this.anims = new ArrayList<Animation>();

        this.keymap = new HashMap<Integer,Keyboard.Keycode>();
        this.keymap.put(SDLKey.SDLK_a,Keyboard.Keycode.A);
        this.keymap.put(SDLKey.SDLK_b,Keyboard.Keycode.B);
        this.keymap.put(SDLKey.SDLK_c,Keyboard.Keycode.C);
        this.keymap.put(SDLKey.SDLK_d,Keyboard.Keycode.D);
        this.keymap.put(SDLKey.SDLK_e,Keyboard.Keycode.E);
        this.keymap.put(SDLKey.SDLK_f,Keyboard.Keycode.F);
        this.keymap.put(SDLKey.SDLK_g,Keyboard.Keycode.G);
        this.keymap.put(SDLKey.SDLK_h,Keyboard.Keycode.H);
        this.keymap.put(SDLKey.SDLK_i,Keyboard.Keycode.I);
        this.keymap.put(SDLKey.SDLK_j,Keyboard.Keycode.J);
        this.keymap.put(SDLKey.SDLK_k,Keyboard.Keycode.K);
        this.keymap.put(SDLKey.SDLK_l,Keyboard.Keycode.L);
        this.keymap.put(SDLKey.SDLK_m,Keyboard.Keycode.M);
        this.keymap.put(SDLKey.SDLK_n,Keyboard.Keycode.N);
        this.keymap.put(SDLKey.SDLK_o,Keyboard.Keycode.O);
        this.keymap.put(SDLKey.SDLK_p,Keyboard.Keycode.P);
        this.keymap.put(SDLKey.SDLK_q,Keyboard.Keycode.Q);
        this.keymap.put(SDLKey.SDLK_r,Keyboard.Keycode.R);
        this.keymap.put(SDLKey.SDLK_s,Keyboard.Keycode.S);
        this.keymap.put(SDLKey.SDLK_t,Keyboard.Keycode.T);
        this.keymap.put(SDLKey.SDLK_u,Keyboard.Keycode.U);
        this.keymap.put(SDLKey.SDLK_v,Keyboard.Keycode.V);
        this.keymap.put(SDLKey.SDLK_w,Keyboard.Keycode.W);
        this.keymap.put(SDLKey.SDLK_x,Keyboard.Keycode.X);
        this.keymap.put(SDLKey.SDLK_y,Keyboard.Keycode.Y);
        this.keymap.put(SDLKey.SDLK_z, Keyboard.Keycode.Z);
        this.keymap.put(SDLKey.SDLK_BACKSPACE, Keyboard.Keycode.Backspace);
        this.keymap.put(SDLKey.SDLK_LEFT, Keyboard.Keycode.Left);
        this.keymap.put(SDLKey.SDLK_RIGHT, Keyboard.Keycode.Right);
        this.keymap.put(SDLKey.SDLK_SPACE, Keyboard.Keycode.Spacebar);
        this.keymap.put(SDLKey.SDLK_0, Keyboard.Keycode.Number_0);
        this.keymap.put(SDLKey.SDLK_1, Keyboard.Keycode.Number_1);
        this.keymap.put(SDLKey.SDLK_2, Keyboard.Keycode.Number_2);
        this.keymap.put(SDLKey.SDLK_3, Keyboard.Keycode.Number_3);
        this.keymap.put(SDLKey.SDLK_4, Keyboard.Keycode.Number_4);
        this.keymap.put(SDLKey.SDLK_5, Keyboard.Keycode.Number_5);
        this.keymap.put(SDLKey.SDLK_6, Keyboard.Keycode.Number_6);
        this.keymap.put(SDLKey.SDLK_7, Keyboard.Keycode.Number_7);
        this.keymap.put(SDLKey.SDLK_8, Keyboard.Keycode.Number_8);
        this.keymap.put(SDLKey.SDLK_9, Keyboard.Keycode.Number_9);
    }

    public Scene add(Layer layer) {
        this.layers.add(layer);
        return this;
    }


    int frameCount = 0;

    public void start() {
        try {
        SDL_Event event = new SDL_Event();
        long start = SDL.SDL_GetTicks();
        while(true) {
            if(frameCount % 30 == 0) {
                p("here: " + frameCount);
            }
            int ret = SDL.SDL_PollEvent(event);
            if(ret == 1) {
                processEvents(event);
                //Util.dump(event);
                if(event.getType() == SDL_EventType.SDL_ACTIVEEVENT) {

                }
                if(event.getType() == SDL_EventType.SDL_QUIT) {
                    break;
                }
                if(event.getType() == SDL_EventType.SDL_MOUSEMOTION) {
                    SDL.SDL_PeepEvents(event,1,SDL_eventaction.SDL_GETEVENT,SDL_EventMask.SDL_MOUSEMOTIONMASK);
                    SDL.SDL_PeepEvents(event,1,SDL_eventaction.SDL_GETEVENT,SDL_EventMask.SDL_MOUSEMOTIONMASK);
                    SDL.SDL_PeepEvents(event,1,SDL_eventaction.SDL_GETEVENT,SDL_EventMask.SDL_MOUSEMOTIONMASK);
                    SDL.SDL_PeepEvents(event,1,SDL_eventaction.SDL_GETEVENT,SDL_EventMask.SDL_MOUSEMOTIONMASK);
                }
            }
            if(shouldStop) {
                break;
            }
            updateAnims();
            draw();
            frameCount++;
            int delay = 1000/targetFPS;
            SDL.SDL_Delay(delay);
        }
        long end = SDL.SDL_GetTicks();
        p("total frames = " + frameCount);
        p("total time = " + (end-start));
        double f = frameCount;
        double d = end-start;
        d = d / 1000;
        double fps = f/d;
        p("fps = " + fps);

        } catch (Throwable thr) {
            thr.printStackTrace();
        }
        SDL.SDL_Quit();
        System.exit(0);
    }

    public void draw() {
        GFX gfx = new JavaExtendedGFX(surface);

        gfx.setPaint(backgroundFill);
        //gfx.setPaint(SColor.GREEN);
        gfx.fillRect(0,0,surface.getW(),surface.getH());
        for(Layer layer : layers) {
            /*
            if(layer.isBuffered()) {
                layer.draw(gfx);
                SDL.SDL_UpperBlit(
                        layer._surface, Util.rect(0, 0, layer._surface.getW(), layer._surface.getH()),
                        _surface, Util.rect(layer.x, layer.y, 0, 0));
            } else {*/
                layer.draw(gfx);
            /*
            } */
        }
        //SDL.SDL_UpdateRect(surface,0,0, surface.getW(),surface.getH());
        //gfx.setPaint(SColor.BLACK);
        //gfx.fillRect(frameCount,20,20,20);
        SDL.SDL_Flip(surface);
        gfx.dispose();
    }

    private void processEvents(SDL_Event event) {
        //Util.dump(event);
        if(event.getType() == SDL_EventType.SDL_MOUSEBUTTONDOWN) {
            mouseDown = true;
            for(Trigger.TriggerRule rule : triggers) {
                if(rule.matches(event)) {
                    rule.call(event);
                }
            }
        }
        if(event.getType() == SDL_EventType.SDL_MOUSEBUTTONUP) {
            mouseDown = false;
            for(Trigger.TriggerRule rule : triggers) {
                if(rule.matches(event)) {
                    rule.call(event);
                }
            }
        }
        if(event.getType() == SDL_EventType.SDL_MOUSEMOTION && mouseDown) {
            MouseEvent evt = new MouseEvent(event.getMotion().getX(),event.getMotion().getY());
            for(Callback<MouseEvent> callback : mouseDragListeners) {
                callback.call(evt);
            }
            for(Trigger.TriggerRule rule : triggers) {
                if(rule.matches(event)) {
                    rule.call(event);
                }
            }
        }
        if(event.getType() == SDL_EventType.SDL_KEYDOWN && !keydown) {
            keydown = true;
            for(Trigger.TriggerRule rule : triggers) {
                if(rule.matches(event)) {
                    rule.call(event);
                }
            }
        }
        if(event.getType() == SDL_EventType.SDL_KEYUP) {
            keydown = false;
        }

        //gesture stuff

        if(event.getType() == SDL_EventType.SDL_MOUSEBUTTONDOWN) {
            RawEvents.RawMouseEvent e = new RawEvents.RawMouseEvent();
            e.x = event.getButton().getX();
            e.y = event.getButton().getY();
            e.pressed = true;
            Gestures.publish(e);
        }

        if(event.getType() == SDL_EventType.SDL_MOUSEBUTTONUP) {
            RawEvents.RawMouseEvent e = new RawEvents.RawMouseEvent();
            e.x = event.getButton().getX();
            e.y = event.getButton().getY();
            e.released = true;
            Gestures.publish(e);
        }

        if(event.getType() == SDL_EventType.SDL_KEYDOWN) {
            RawEvents.RawKeyEvent e = new RawEvents.RawKeyEvent();
            int sym = event.getKey().getKeysym().getSym();
            int mod = event.getKey().getKeysym().getMod();
            if(!keymap.containsKey(sym)) {
                Util.warn("keymap doesn't contain the symbol: " + sym + " : " +
                        SDL.SDL_GetKeyName(sym)
                );
                return;
            }
            e.keyCode = keymap.get(sym);
            e.pressed = true;
            if((mod & SDLMod.KMOD_LSHIFT) > 0) e.shiftPressed = true;
            if((mod & SDLMod.KMOD_RSHIFT) > 0) e.shiftPressed = true;
            if((mod & SDLMod.KMOD_LMETA)  > 0) e.commandPressed = true;
            Gestures.publish(e);
        }

        if(event.getType() == SDL_EventType.SDL_KEYUP) {
            RawEvents.RawKeyEvent e = new RawEvents.RawKeyEvent();
            int sym = event.getKey().getKeysym().getSym();
            int mod = event.getKey().getKeysym().getMod();
            if(!keymap.containsKey(sym)) {
                Util.warn("keymap doesn't contain the symbol: " + sym);
                return;
            }
            e.keyCode = keymap.get(sym);
            e.released = true;
            if((mod & SDLMod.KMOD_LSHIFT) > 0) e.shiftPressed = true;
            if((mod & SDLMod.KMOD_RSHIFT) > 0) e.shiftPressed = true;
            if((mod & SDLMod.KMOD_LMETA)  > 0) e.commandPressed = true;
            Gestures.publish(e);
        }


    }

    private void updateAnims() {
        ListIterator<Animation> it = anims.listIterator();
        while(it.hasNext()) {
            try {
                long currentTime = SDL.SDL_GetTicks();
                Animation a = it.next();
                if(a.isDead()) {
                    it.remove();
                    continue;
                }
                if(!a.isRunning()) {
                    a.start(currentTime);
                }
                a.update(currentTime);

            } catch (Throwable thr) {
                thr.printStackTrace();
            }
        }
    }

    public void setBackgroundFill(SColor backgroundFill) {
        this.backgroundFill = backgroundFill;
    }

    public void listen(Events event, Callback<MouseEvent> callback) {
        this.listeners.add(callback);
        if(event == Events.MouseDrag) {
            this.mouseDragListeners.add(callback);
        }
    }

    public void listen(Trigger.TriggerRule trigger) {
        this.triggers.add(trigger);
    }

    public void animate(Animation animation) {
        this.anims.add(animation);
    }

    public Scene setTargetFPS(int targetFPS) {
        this.targetFPS = targetFPS;
        return this;
    }

    public void exit() {
        this.shouldStop = true;
    }
}
